package client;

import java.io.Serializable;

import miscellaneous.Storage;
import mockup.Mockup;
import mockup.MockupFactory;
import mockup.MockupToData;
import client.tcp.ClientTCP;
import data.Data;

/**
 * Synchronizer is a module that contacts the other end of the socket (the server).
 * It can get objects from the server, create and delete objects on the server, and commit changes to it.
 * @author Erik
 *
 */
public class Synchronizer<E extends Data> { // implements PropertyChangeListener {
	
	public static final int SUCCESSFUL = 2000;
	public static final int REFUSED = 2001;
	public static final int FAILED = 2002;
	
	private Class<E> dataClass;
	
	/**
	 * Creates a synchronizer object to handle communication of objects of type E
	 * @param dataClass The class that should be communicated
	 */
	public Synchronizer(Class<E> dataClass) {
		this.dataClass = dataClass;
	}
	
	/**
	 * Updates the object on the server. If the change is successful, it returns the result of the action.
	 * @return How the update was completed:<br>
	 * &nbsp;&nbsp;&nbsp;SUCCESSFUL if the update was completed<br>
	 * &nbsp;&nbsp;&nbsp;REFUSED  if the update was rejected by the server<br>
	 * &nbsp;&nbsp;&nbsp;FAILED  if the update failed
	 */
	public int update(E data) {
		ClientTCP socket = Storage.getSocket();
		if(socket != null && socket.isConnected()) {
			Mockup mockup = MockupFactory.getMockup(data);
			mockup.setState(Mockup.CHANGE);
			mockup.setSessionId(Storage.getSessionID());
			Serializable received  = socket.send(mockup);
			if(received instanceof Mockup) {
				int id = ((Mockup)received).getId();
				if(id == data.getId())
					return SUCCESSFUL;
				else return REFUSED;
			}
			else {
				System.err.println("Synchronizer::update: received an object not instanceof Mockup");
				return FAILED;
			}
		}
		return FAILED;
	}
	
	/**
	 * Removes the supplied object from the server
	 * @param data The object to remove
	 * @return How the remove was completed:
	 * &nbsp;&nbsp;&nbsp;SUCCESSFUL if the object was removed from the server<br>
	 * &nbsp;&nbsp;&nbsp;REFUSED if the remove was rejected by the server<br>
	 * &nbsp;&nbsp;&nbsp;FAILED if the remove failed
	 */
	public int delete(E data) {
		ClientTCP socket = Storage.getSocket();
		if(socket != null && socket.isConnected()) {
			Mockup mockup = MockupFactory.getMockup(data);
			mockup.setState(Mockup.DELETE);
			mockup.setSessionId(Storage.getSessionID());
			
			Serializable received = socket.send(mockup);
			if(received instanceof Mockup) {
				int id = ((Mockup)received).getId();
				if(id == data.getId())
					return SUCCESSFUL;
				else
					return REFUSED;
			}
			else
				System.err.println("Synchronizer::remove: recieved an object that wasn't a Mockup");
		}
		return FAILED;
	}

	/**
	 * Creates the supplied object on the server
	 * @param data The object to add
	 * @return How the creation was completed:
	 * &nbsp;&nbsp;&nbsp;SUCCESSFUL if the object was created on the server<br>
	 * &nbsp;&nbsp;&nbsp;REFUSED if the create was rejected by the server<br>
	 * &nbsp;&nbsp;&nbsp;FAILED if the create failed
	 */
	public int create(E data) {
		ClientTCP socket = Storage.getSocket();
		if(socket != null && socket.isConnected()) {
			Mockup mockup = MockupFactory.getMockup(data);
			mockup.setState(Mockup.CREATE);
			mockup.setSessionId(Storage.getSessionID());
	
			Serializable received = socket.send(mockup);
			if(received instanceof Mockup) {
				int id = ((Mockup)received).getId();
				if(id <= 0)
					return REFUSED;
				else
					return SUCCESSFUL;
			}
			else
				System.err.println("Synchronizer::create: recieved an object that wasn't a Mockup");
		}
		return FAILED;
	}
	
	/**
	 * Retrieves the object of class E with the supplied id from the server and returns it.
	 * @param id The id of the object to be retrieved
	 * @return The retrieved object if one is found, {@code null} if none is found.
	 */
	@SuppressWarnings("unchecked")
	public E get(int id) {
		ClientTCP socket = Storage.getSocket();
		if(socket != null && socket.isConnected()) {
			Mockup mockup = MockupFactory.getEmptyMockup(dataClass);
			mockup.setId(id);
			mockup.setState(Mockup.GET);
			mockup.setSessionId(Storage.getSessionID());
			
			Serializable answer = socket.send(mockup);
			if(answer instanceof Mockup) {
				if(((Mockup)answer).getId() == id) {
					Data data = MockupToData.getData((Mockup)answer);
					if(data.getClass() == dataClass) {
						return (E) data;
					} else
						System.err.println("Synchronizer::get: Received an object of another type than expected");
				} else
					System.err.println("Synchronizer::get: Server refused get of object");
					
			} else
				System.err.println("Synchronizer::get: Received an object that isn't a mockup");
		} else
			System.err.println("Synchronizer::get: Socket is null or not connected");

		return null;
		
	}
}
